//
//  JSMIDI.m
//
//  Modified from Occam by C. Ramakrishnan
//

#import "JSMIDI.h"
#import "PyobjCMidiPlugIn.h"

/*
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
*/

#define DEFAULT_PORT 57117

@interface JSMIDI (Private)

- (void)sendChannelCommandStatus:(char)status channel:(int)channel byte1:(char)byte1 byte2:(char)byte2;

@end

@implementation JSMIDI

#pragma mark _____ superclass overrides
- (void)dealloc
{
	MIDIClientDispose(_midiClient);
	MIDIEndpointDispose(_source);
	if (_outputPort)
		MIDIPortDispose(_outputPort);
	[super dealloc];
}

#pragma mark _____ initialization
- (id)initWithController:(PyobjCMidiPlugIn*)controller;
{
   self = [super init];
   if (self) {
      OSStatus err;
      err = MIDIClientCreate(CFSTR("PyobjCMidi"), NULL, NULL, &_midiClient);
      if (err)
      {
         NSLog(@"Couldn't create client %i", err);
         return nil;
      }
      
      err = MIDISourceCreate(_midiClient, (CFStringRef) [self midiSourceName], &_source);
      if (err)
      {
         NSLog(@"Couldn't create source %i", err);
         MIDIClientDispose(_midiClient);
         return nil;
      }
         
      err = MIDIOutputPortCreate(_midiClient, CFSTR("PyobjCMidi Loopback Output"), &_outputPort);
      if (err)
         NSLog(@"Couldn't create output port %i", err);
      
      _controller = controller;
   }
   return self;		
}

#pragma mark _____ private methods

- (void)sendChannelCommandStatus:(char)status channel:(int)channel byte1:(char)byte1 byte2:(char)byte2
{
	OSStatus err;
	MIDIPacketList packets;
	int sentChannel = channel -1; // send a 0 for channel 1
	
	packets.numPackets = 1;
	packets.packet[0].timeStamp = 0; // now
	packets.packet[0].length = 3;    // 3 bytes long

	// status byte 
	// --4 bits command
	// --then 4 bits channel (I mask just to be safe)
	packets.packet[0].data[0] = status | (15 & sentChannel);

	// key
	packets.packet[0].data[1] = byte1;
	
	// velocity
	packets.packet[0].data[2] = byte2;
	
	err = MIDIReceived(_source, &packets);
	if (err)
	{
		NSLog(@"Couldn't send MIDI %i", err);
	}
}

#pragma mark _____ Midi actions
- (void)statusMessage:(NSString*)message
{
	[_controller statusMessage: message];
}

- (void)sendNoteOnChannel:(int)channel key:(int)key velocity:(int)velocity
{
	[self sendChannelCommandStatus: 0x90 channel: channel byte1: key byte2: velocity];
}

- (void)sendNoteOffChannel:(int)channel key:(int)key velocity:(int)velocity
{
	[self sendChannelCommandStatus: 0x80 channel: channel byte1: key byte2: velocity];
}

- (void)sendPolyPressureOnChannel:(int)channel key:(int)key pressure:(int)pressure
{
	[self sendChannelCommandStatus: 0xA0 channel:channel byte1:key byte2:pressure];
}

- (void)sendControlChangeOnChannel:(int)channel index:(int)index value:(int)value
{
	[self sendChannelCommandStatus: 0xB0 channel:channel byte1:index byte2: value];
}

- (void)sendProgramChangeOnChannel:(int)channel index:(int)index
{
	[self sendChannelCommandStatus: 0xC0 channel:channel byte1:index byte2: 0];
}

- (void)sendChannelPressureOnChannel:(int)channel pressure:(int)pressure
{
	[self sendChannelCommandStatus: 0xD0 channel:channel byte1:pressure byte2: 0];
}

- (void)sendPitchWheelOnChannel:(int)channel value:(int)value
{
	// bits 1 -7
	char lsb = 0x7F & value;		// mask the 8th bit

	// bits 8 -14
	char msb = 0x7f & (value >> 7); // mask the 8th bit

	[self sendChannelCommandStatus: 0xE0 channel: channel byte1: lsb byte2: msb];
}



#pragma mark _____ accessors

- (NSString*)midiSourceName
{
	return @"PyobjCMidi";
}



@end

